﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using WeixinPayResdk;
using WeixinPayResdk.Models;
using WeixinPayResdk.Helper;

namespace WeixinPayDemoWebAPI.Controllers
{
    public class WeixinPayController : ApiController
    {
        private string appid = WeixinPayConfig.Appid;
        private string merchantId = WeixinPayConfig.merchantId;
        private string description = WeixinPayConfig.description;
        private string notifyUrl = WeixinPayConfig.notifyUrl;
        private string serialNo = WeixinPayConfig.serialNo;
        /// <summary>
        /// 微信支付统一下单
        /// </summary>
        /// <param name="openid"></param>
        /// <param name="orderId"></param>
        /// <param name="total"></param>
        /// <returns></returns>
        public async Task<Dictionary<string, string>> WeixinRSA2Pay(string openid, string orderId, int total)
        {
            var req = new
            {
                appid = appid,
                mchid = merchantId,
                description = description,
                amount = new
                {
                    total = total,
                    currency = "CNY"
                },
                payer = new
                {
                    openid = openid
                },
                out_trade_no = orderId,
                notify_url = notifyUrl
            };

            HttpClient client = new HttpClient(new WeixinPayHelper());

            //一定要这样传递参数，不然在加密签名的时候获取到的参数就是\\u0这种形式的数据了，不是传递的这样的数据了，导致加密的结果不正确
            var bodyJson = new StringContent(JsonConvert.SerializeObject(req), Encoding.UTF8, "application/json");
            string transactionsjsapi = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
            var resp = await client.PostAsync(transactionsjsapi, bodyJson);
            // 读取统一下单之后的返回结果，这样读取出来的直接就是结果，或者错误原因。
            var respStr = await resp.Content.ReadAsStringAsync();
            //这里面就包含prepay_id了
            var result = JsonConvert.DeserializeObject<WeixinSignResModel>(respStr);
            var res = new Dictionary<string, string>();
            if (!string.IsNullOrEmpty(result.prepay_id))
            {
                //时间戳
                var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
                //随机数
                var nonMun = Guid.NewGuid().ToString("N").Substring(0, 30);
                var pck = "prepay_id=" + result.prepay_id;
                //签名
                string message = $"{appid}\n{timestamp}\n{nonMun}\n{pck}\n";
                string keyRsa = WeixinPayHelper.Sign(message);
                var reqParm = new
                {
                    appId = appid,
                    timeStamp = timestamp,
                    nonceStr = nonMun,
                    package = pck,
                    signType = "RSA",
                    paySign = keyRsa,
                    orderId = orderId
                };
                res.Add("appId", reqParm.appId);
                res.Add("timeStamp", reqParm.timeStamp);
                res.Add("nonceStr", reqParm.nonceStr);
                res.Add("package", reqParm.package);
                res.Add("signType", reqParm.signType);
                res.Add("paySign", reqParm.paySign);
                res.Add("orderId", reqParm.orderId);
                return res;
            }
            else
            {
                res.Add("msg", "prepay_id获取失败:" + respStr);
                return res;
            }
        }

        /// <summary>
        /// 查询订单 按商户订单号查询
        /// </summary>
        /// <returns></returns>
        private async Task<WeixinPayStatusResModel> QueryOrder(string orderNumber)
        {
            string privateKey = ReadFile.APIClientKey(WeixinPayConfig.privateKeyFile);
            var helper = new WeixinPayHelper();
            var payModel = await helper.QueryOrder(orderNumber);
            return payModel;
        }

        /// <summary>
        /// 微信支付v3 回调
        /// </summary>
        /// <returns></returns>
        public async Task<WeixinPayCallbackRespModel> WeixinPayNotify()
        {
            var context = HttpContext.Current;
            context.Response.ContentType = "application/json";
            //获取回调POST过来的Header数据
            string timestamp = context.Request.Headers["Wechatpay-Timestamp"].ToString();
            string nonce = context.Request.Headers["Wechatpay-Nonce"].ToString();
            string signature = context.Request.Headers["Wechatpay-Signature"].ToString();
            string serial = context.Request.Headers["Wechatpay-Serial"].ToString();
            //获取回调POST过来的Body数据
            Stream stream = context.Request.InputStream;
            int count = 0;
            byte[] buffer = new byte[1024];
            StringBuilder builder = new StringBuilder();
            while ((count = stream.Read(buffer, 0, 1024)) > 0)
            {
                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
            }
            stream.Flush();
            stream.Close();
            stream.Dispose();

            //不验签 查询微信订单API 再走业务逻辑
            return await VerifyNoSign(buffer);

            //验签后 查询微信订单API 再走业务逻辑 需大神修复验签不过的问题
            return await VerifySign(timestamp, nonce, signature, builder.ToString());
        }

        /// <summary>
        /// 验签后 查询微信订单API 再走业务逻辑 需大神修复验签不过的问题
        /// </summary>
        /// <param name="timestamp"></param>
        /// <param name="nonce"></param>
        /// <param name="signature"></param>
        /// <param name="body"></param>
        /// <returns></returns>
        private async Task<WeixinPayCallbackRespModel> VerifySign(string timestamp, string nonce, string signature, string body)
        {
            var context = HttpContext.Current;
            string signSourceString = $"{timestamp}\n{nonce}\n{body}\n";
            string publickey = ReadFile.APIClientCert(WeixinPayConfig.apiclientCertFile);
            bool signResult = AesGcmHelper.VerifySign(signature, signSourceString, publickey);
            var res = new WeixinPayCallbackRespModel();
            res.code = "FAIL";
            if (!signResult)
            {
                res.message = "验签失败";
                return res;
            }
            WeixxinPayNotifyModel notifyMap = (WeixxinPayNotifyModel)JsonConvert.DeserializeObject(body);
            string resData = AesGcmHelper.AesGcmDecrypt(notifyMap.resource.associated_data, nonce, notifyMap.resource.ciphertext, WeixinPayConfig.aesKey);
            
            var weixinOrder = resData.ToObject<WeixinPayResDesModel>();
            //微信订单号
            string transaction_id = weixinOrder.transaction_id;
            //商户订单号
            string out_trade_no = weixinOrder.out_trade_no;
            //交易状态
            string trade_state = weixinOrder.trade_state;
            //实付金额，单位分
            int payer_total = weixinOrder.amount.payer_total;

            if (trade_state != "SUCCESS")
            {
                res.message = "支付失败";
                return res;
            }

            //查询订单，判断订单真实性
            var resp = await QueryOrder(out_trade_no);
            if (resp.trade_state == "SUCCESS")
            {
                //走商户逻辑
                //to do ...
                res.code = "SUCCESS";
                res.message = "支付完成";
            }
            else
            {
                //若订单查询失败，则立即返回结果给微信支付后台
                res.message = "订单查询失败";
            }
            return res;
        }

        /// <summary>
        /// 不验签 查询微信订单API 再走业务逻辑
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        private async Task<WeixinPayCallbackRespModel> VerifyNoSign(byte[] buffer)
        {
            var context = HttpContext.Current;
            //没有使用官方的那种验证数据安全性的方法，解密出来数据之后，直接拿着订单号再去查询一下订单状态，然后再更新到数据库中。
            var str = Encoding.UTF8.GetString(buffer);
            var wxPayNotifyModel = JsonConvert.DeserializeObject<WeixxinPayNotifyModel>(str);
            var resource = wxPayNotifyModel?.resource ?? new WeixinPayResourceModel();
            var decryptStr = AesGcmHelper.AesGcmDecrypt(resource.associated_data, resource.nonce, resource.ciphertext, WeixinPayConfig.aesKey);
            var decryptModel = JsonConvert.DeserializeObject<WeixinPayResDesModel>(decryptStr);
            var res = new WeixinPayCallbackRespModel();
            res.code = "FAIL";
            if (string.IsNullOrEmpty(decryptModel.out_trade_no))
            {
                res.message = "数据解密失败";
            }
            else
            {
                //查询订单，判断订单真实性
                var resp = await QueryOrder(decryptModel.out_trade_no);
                if (resp.trade_state == "SUCCESS")
                {
                    //走商户逻辑
                    //to do ...
                    res.code = "SUCCESS";
                    res.message = "成功";
                }
                else
                {
                    res.message = "支付失败";
                }
            }
            return res;
        }


        /// <summary>
        /// 关闭订单
        /// </summary>
        /// <param name="orderNumber"></param>
        /// <returns></returns>
        public async Task<ClostOrderRespModel> CloseOrder(string orderNumber)
        {
            string privateKey = ReadFile.APIClientKey(WeixinPayConfig.privateKeyFile);
            var helper = new WeixinPayHelper();
            var payModel = await helper.ClostOrder(orderNumber);
            return payModel;
        }

        /// <summary>
        /// 退款订单
        /// </summary>
        /// <param name="orderNumber"></param>
        /// <returns></returns>
        public async Task<RefundsRespModel> Refunds(string orderNumber)
        {
            var helper = new WeixinPayHelper();
            var refundNumber = $"{DateTime.Now:yyyyMMddHHmmssff}{new Random().Next(100,999)}";
            var payModel = await helper.Refunds(orderNumber, refundNumber, "测试退款", 1, 1);
            return payModel;
        }

        /// <summary>
        /// 退款通知回调接口
        /// </summary>
        /// <returns></returns>
        public async Task<WeixinPayCallbackRespModel> refundsNotify()
        {
            #region 获取字符串流
            System.IO.Stream s = HttpContext.Current.Request.InputStream;
            int count = 0;
            byte[] buffer = new byte[1024];
            StringBuilder builder = new StringBuilder();
            while ((count = s.Read(buffer, 0, 1024)) > 0)
            {
                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
            }
            s.Flush();
            s.Close();
            s.Dispose();
            //var buffer = new MemoryStream();
            //Request.Body.CopyTo(buffer);
            #endregion
            //没有使用官方的那种验证数据安全性的方法，解密出来数据之后，直接拿着商户退款订单号再去查询一下订单状态，然后再更新到数据库中。

            var str = Encoding.UTF8.GetString(buffer);
            var wxPayNotifyModel = str.ToObject<RefundsCallbackModel>();
            var resource = wxPayNotifyModel?.resource ?? new RefundsCallbackResourceModel();
            var decryptStr = AesGcmHelper.AesGcmDecrypt(resource.associated_data, resource.nonce, resource.ciphertext, WeixinPayConfig.aesKey);
            var decryptModel = decryptStr.ToObject<RefundsCallbackDecryptModel>();

            var viewModel = new WeixinPayCallbackRespModel();
            viewModel.code = "FAIL";
            if (string.IsNullOrEmpty(decryptModel.out_trade_no))
            {
                viewModel.message = "数据解密失败";
            }
            else
            {
                var resp = await QueryRefunds(decryptModel.out_refund_no);
                //然后进行数据库更新处理……等等其他操作
                viewModel.code = "SUCCESS";
                viewModel.message = "退款处理成功";
            }
            return viewModel;
        }

        /// <summary>
        /// 查询退款结果接口
        /// </summary>
        /// <param name="refundNumber"></param>
        /// <returns></returns>
        public async Task<QueryRefundsOrderRespModel> QueryRefunds(string refundNumber)
        {
            var helper = new WeixinPayHelper();
            var payModel = await helper.QueryRefundsOrder(refundNumber);
            return payModel;
        }
    }
}
